/* 
 * Copyright (C) 2008 Torgny Bjers
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.sugree.jibjib.data;

import java.net.SocketTimeoutException;
import java.util.Date;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteConstraintException;
import android.net.Uri;
import android.text.Html;
import android.util.Log;

import com.sugree.jibjib.data.JibJibDatabase.Tweets;
import com.sugree.jibjib.net.Connection;
import com.sugree.jibjib.net.ConnectionAuthenticationException;
import com.sugree.jibjib.net.ConnectionException;
import com.sugree.jibjib.net.ConnectionUnavailableException;

/**
 * Handles loading data from JSON into database.
 * 
 * @author torgny.bjers
 */
public class FriendTimeline {

	private static final String TAG = "FriendTimeline";

	private ContentResolver mContentResolver;
	private String mUsername, mPassword;
	private long mLastStatusId = 0;
	private int mNewTweets;
	private int mReplies;
	private String mGateway = null;

	public FriendTimeline(ContentResolver contentResolver, String username, String password, long lastStatusId) {
		mContentResolver = contentResolver;
		mUsername = username;
		mPassword = password;
		mLastStatusId = lastStatusId;
	}

	public void setGateway(String gateway) {
		mGateway = gateway;
	}

	/**
	 * Load the user and friends timeline.
	 * 
	 * @throws ConnectionException
	 * @throws JSONException
	 * @throws SQLiteConstraintException
	 * @throws ConnectionAuthenticationException
	 * @throws ConnectionUnavailableException
	 * @throws SocketTimeoutException 
	 */
	public void loadTimeline(SharedPreferences sp) throws ConnectionException, JSONException, SQLiteConstraintException, ConnectionAuthenticationException, ConnectionUnavailableException, SocketTimeoutException {
		loadTimeline(sp, JibJibDatabase.Tweets.TWEET_TYPE_TWEET, false);
	}

	/**
	 * Load the user and friends timeline.
	 * 
	 * @param tweetType
	 * @throws ConnectionException
	 * @throws JSONException
	 * @throws SQLiteConstraintException
	 * @throws ConnectionAuthenticationException
	 * @throws ConnectionUnavailableException
	 * @throws SocketTimeoutException 
	 */
	public void loadTimeline(SharedPreferences sp, int tweetType) throws ConnectionException, JSONException, SQLiteConstraintException, ConnectionAuthenticationException, ConnectionUnavailableException, SocketTimeoutException {
		loadTimeline(sp, tweetType, false);
	}

	/**
	 * Load the user and friends timeline.
	 * 
	 * @param tweetType
	 * @param firstRun
	 * @throws ConnectionException
	 * @throws JSONException
	 * @throws SQLiteConstraintException
	 * @throws ConnectionAuthenticationException
	 * @throws ConnectionUnavailableException
	 * @throws SocketTimeoutException 
	 */
	public void loadTimeline(SharedPreferences sp, int tweetType, boolean firstRun) throws ConnectionException, JSONException, SQLiteConstraintException, ConnectionAuthenticationException, ConnectionUnavailableException, SocketTimeoutException {
		mNewTweets = 0;
		mReplies = 0;
		int limit = 200;
		if (firstRun) {
			limit = 20;
		}
		if ((mUsername != null && mUsername.length() > 0) || sp.getBoolean("use_oauth", false)) {
			/*
			Connection aConn = new Connection(mUsername, mPassword, mLastStatusId, limit);
			aConn.setTwitterGateway(mGateway);
			*/
			Connection aConn = new Connection(sp);
			aConn.setSinceId(mLastStatusId);
			aConn.setLimit(limit);
			JSONArray jArr = null;
			switch (tweetType) {
			case JibJibDatabase.Tweets.TWEET_TYPE_TWEET:
				jArr = aConn.getFriendsTimeline();
				break;
			case JibJibDatabase.Tweets.TWEET_TYPE_REPLY:
				jArr = aConn.getMentionsTimeline();
				break;
			default:
				Log.e(TAG, "Got unhandled tweet type: " + tweetType);
				break;
			}
			for (int index = 0; index < jArr.length(); index++) {
				JSONObject jo = jArr.getJSONObject(index);
				long lId = jo.getLong("id");
				insertFromJSONObject(jo, tweetType);
				if (lId > mLastStatusId) {
					mLastStatusId = lId;
				}
			}
			if (mNewTweets > 0) {
				mContentResolver.notifyChange(JibJibDatabase.Tweets.CONTENT_URI, null);
			}
		}
	}

	/**
	 * Insert a row from a JSONObject.
	 * 
	 * @param jo
	 * @param tweetType
	 * @return
	 * @throws JSONException
	 * @throws SQLiteConstraintException
	 */
	public Uri insertFromJSONObject(JSONObject jo, int tweetType) throws JSONException, SQLiteConstraintException {
		JSONObject user;
		user = jo.getJSONObject("user");

		ContentValues values = new ContentValues();

		// Construct the Uri to existing record
		Long lTweetId = Long.parseLong(jo.getString("id"));
		Uri aTweetUri = ContentUris.withAppendedId(JibJibDatabase.Tweets.CONTENT_URI, lTweetId);

		values.put(JibJibDatabase.Tweets._ID, lTweetId.toString());
		values.put(JibJibDatabase.Tweets.AUTHOR_ID, user.getString("screen_name"));

		String message = Html.fromHtml(jo.getString("text")).toString();
		values.put(JibJibDatabase.Tweets.MESSAGE, message);
		values.put(JibJibDatabase.Tweets.SOURCE, jo.getString("source"));
		values.put(JibJibDatabase.Tweets.TWEET_TYPE, tweetType);
		values.put(JibJibDatabase.Tweets.IN_REPLY_TO_STATUS_ID, jo.getString("in_reply_to_status_id"));
		values.put(JibJibDatabase.Tweets.IN_REPLY_TO_AUTHOR_ID, jo.getString("in_reply_to_screen_name"));
		values.put(JibJibDatabase.Tweets.FAVORITED, jo.getBoolean("favorited") ? 1 : 0);
		values.put(JibJibDatabase.Tweets.PROTECTED, user.getBoolean("protected") ? 1 : 0);
		try {
			JSONArray coords = jo.getJSONObject("geo").getJSONArray("coordinates");
			values.put(JibJibDatabase.Tweets.LATITUDE, coords.getDouble(0));
			values.put(JibJibDatabase.Tweets.LONGITUDE, coords.getDouble(1));
		} catch (JSONException e) {
		}

		try {
			Long created = Date.parse(jo.getString("created_at"));
			values.put(Tweets.SENT_DATE, created);

			if ((mContentResolver.update(aTweetUri, values, null, null)) == 0) {
				mContentResolver.insert(JibJibDatabase.Tweets.CONTENT_URI, values);
				mNewTweets++;
				if (mUsername.equals(jo.getString("in_reply_to_screen_name")) || message.contains("@" + mUsername)) {
					mReplies++;
				}
			}
		} catch (Exception e) {
			Log.e(TAG, e.getMessage(), e);
		}

		return aTweetUri;
	}

	/**
	 * Insert a row from a JSONObject.
	 * Takes an optional parameter to notify listeners of the change.
	 * 
	 * @param jo
	 * @param tweetType
	 * @param notify
	 * @return Uri
	 * @throws JSONException
	 * @throws SQLiteConstraintException
	 */
	public Uri insertFromJSONObject(JSONObject jo, int tweetType, boolean notify) throws JSONException, SQLiteConstraintException {
		Uri aTweetUri = insertFromJSONObject(jo, tweetType);
		if (notify) mContentResolver.notifyChange(aTweetUri, null);
		return aTweetUri;
	}

	/**
	 * Remove old records to ensure that the database does not grow too large.
	 * 
	 * @param sinceTimestamp
	 * @return Number of deleted records
	 */
	public int pruneOldRecordsSinceTimestamp(long sinceTimestamp) {
		if (sinceTimestamp == 0) {
			sinceTimestamp = System.currentTimeMillis();
		}
		return mContentResolver.delete(JibJibDatabase.Tweets.CONTENT_URI, JibJibDatabase.Tweets.CREATED_DATE + " < " + sinceTimestamp, null);
	}

	/**
	 * Remove old records to ensure that the database does not grow too large.
	 * 
	 * @param size
	 * @return Number of deleted records
	 */
	public int pruneOldRecordsBySize(int size) {
		int count = 0;
		Cursor c = mContentResolver.query(
			Tweets.CONTENT_URI,
			new String[] { Tweets._ID },
			JibJibDatabase.Tweets.TWEET_TYPE + " = ?",
			new String[] { String.valueOf(Tweets.TWEET_TYPE_TWEET) },
			null);
		if (c.getCount() > size*2) {
			long sinceId = 0;
			try {
				c.moveToPosition(size);
				sinceId = c.getLong(c.getColumnIndex(Tweets._ID));
			} catch (Exception e) {
				Log.e(TAG, e.getMessage());
			} finally {
				if (c != null && !c.isClosed()) c.close();
			}
			if (sinceId > 0) {
				count += mContentResolver.delete(
					JibJibDatabase.Tweets.CONTENT_URI,
					JibJibDatabase.Tweets.TWEET_TYPE + " = ? AND " + JibJibDatabase.Tweets._ID + " <= ?",
					new String[] { String.valueOf(JibJibDatabase.Tweets.TWEET_TYPE_TWEET), String.valueOf(sinceId) });
			}
		} else {
			c.close();
		}

		c = mContentResolver.query(
			Tweets.CONTENT_URI,
			new String[] { Tweets._ID },
			JibJibDatabase.Tweets.TWEET_TYPE + " = ?",
			new String[] { String.valueOf(Tweets.TWEET_TYPE_REPLY) },
			null);
		if (c.getCount() > size*2) {
			long sinceId = 0;
			try {
				c.moveToPosition(size);
				sinceId = c.getLong(c.getColumnIndex(Tweets._ID));
			} catch (Exception e) {
				Log.e(TAG, e.getMessage());
			} finally {
				if (c != null && !c.isClosed()) c.close();
			}
			if (sinceId > 0) {
				count += mContentResolver.delete(
					JibJibDatabase.Tweets.CONTENT_URI,
					JibJibDatabase.Tweets.TWEET_TYPE + " = ? AND " + JibJibDatabase.Tweets._ID + " <= ?",
					new String[] { String.valueOf(JibJibDatabase.Tweets.TWEET_TYPE_REPLY), String.valueOf(sinceId) });
			}
		} else {
			c.close();
		}

		return count;
	}

	/**
	 * Return the number of new statuses.
	 * 
	 * @return integer
	 */
	public int newCount() {
		return mNewTweets;
	}

	/**
	 * Return the number of new replies.
	 * 
	 * @return integer
	 */
	public int replyCount() {
		return mReplies;
	}

	/**
	 * Get the last status ID.
	 * 
	 * @return long
	 */
	public long lastId() {
		return mLastStatusId;
	}

	/**
	 * Destroy the status specified by ID.
	 * 
	 * @param statusId
	 * @return Number of deleted records
	 */
	public int destroyStatus(long statusId) {
		return mContentResolver.delete(JibJibDatabase.Tweets.CONTENT_URI, JibJibDatabase.Tweets._ID + " = " + statusId, null);
	}

	/*
	public void setFavorite(long statusId, boolean flag) {
		Uri aTweetUri = ContentUris.withAppendedId(JibJibDatabase.Tweets.CONTENT_URI, new Long(statusId));

		ContentValues values = new ContentValues();
		values.put(JibJibDatabase.Tweets.FAVORITED, flag ? 1 : 0);
		mContentResolver.update(aTweetUri, values, null, null);
	}
	*/
}
